﻿using Nest;
using PMS.Search.Domain.Entities;
using PMS.Search.Elasticsearch;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ElasticSearchTest
{
    public class UniversityEsService
    {
        private readonly EsClient _client;
        private readonly EsIndexConfig.Index _index = EsIndexConfig.Instance.UniversityIndex;

        public UniversityEsService(EsClient client)
        {
            _client = client;
        }
        public List<SearchUniversity> SearchUniversitys(string keyword, int? cityId, int? type, int pageIndex, int pageSize)
        {
            int from = (pageIndex - 1) * pageSize;
            int size = pageSize;

            var search = new SearchDescriptor<SearchUniversity>()
              .Index(_index.Alias)
              .From(from)
              .Size(size)
              .Sort(s => s
                  .Descending(SortSpecialField.Score)
                  .Ascending(item => item.FirstLetterName)
                  .Ascending(item => item.Type)
              )
              ;

            var mustQuery = new List<Func<QueryContainerDescriptor<SearchUniversity>, QueryContainer>>() {
                q => q.Term(t=> t.Field(s => s.IsDeleted).Value(false)),
            };

            var shouldQuerys = new List<Func<QueryContainerDescriptor<SearchUniversity>, QueryContainer>>
            {
                q => q.Match(t => t.Field("name").Query(keyword).Boost(2)),
                q => q.Term(t => t.Field("name.pinyin").Value(keyword).Boost(2)),
            };

            //关键字查询
            if (!string.IsNullOrWhiteSpace(keyword))
            {
                mustQuery.Add(q => q.Match(t => t.Field("name.pinyin").Query(keyword)));
            }
            //城市查询
            if (cityId > 0)
            {
                mustQuery.Add(q => q.Term(t => t.Field(f => f.CityId).Value(cityId)));
            }
            //学校类型查询
            if (type > 0)
            {
                mustQuery.Add(q => q.Term(t => t.Field(f => f.Type).Value(type)));
                mustQuery.Add(q => q.Script(s => s
                    .Script(ss => ss.Source($"  (((int)doc['type'].value) & {type}) > 0  "))
                ));
            }

            Func<QueryContainerDescriptor<SearchUniversity>, QueryContainer> query = q => q.Bool(b => b.Must(mustQuery));
            search.Query(q =>
                q.FunctionScore(fs=>fs
                    .Query(query)
                    .BoostMode(FunctionBoostMode.Replace)
                    .Functions(fun=> fun
                        .ScriptScore(
                            ss=>ss.Script(sss=>sss.Source(" Math.round(_score * 10) ")))
                        )
                ));

            var result = _client.GetClient().Search<SearchUniversity>(search);
            var data = result.Hits.Select(s => s.Source).ToList();

            return data;
        }

        public CreateIndexResponse CreateIndex()
        {
            var resp = _client.GetClient().Indices.Delete(_index.Name);
            return _client.GetClient().Indices.Create(_index.Name, c => c
                .Aliases(ad => ad.Alias(_index.Alias))
                .Settings(s => s
                    .Setting("max_ngram_diff", 20)
                    .Setting("max_result_window", 500000)
                    .Analysis(a => a
                        .CharFilters(cf => cf
                            .UserDefined("tsconvert", new TSConvertCharFilter("t2s", "#"))
                         )
                        .TokenFilters(tf => tf
                            .UserDefined("pinyin_max_filter",
                                new PinyinTokenFilter
                                {
                                    KeepSeparateFirstLetter = false,
                                    KeepJoinedFullPinyin = true,
                                    KeepFullPinyin = true,
                                    KeepOriginal = true,
                                    LimitFirstLetterLength = 16,
                                    Lowercase = true,
                                    RemoveDuplicatedTerm = true
                                })
                            .UserDefined("pinyin_filter",
                                new PinyinTokenFilter
                                {
                                    KeepFirstLetter = false,
                                    KeepSeparateFirstLetter = false,
                                    KeepJoinedFullPinyin = true,
                                    KeepFullPinyin = false,
                                    KeepOriginal = true,
                                    LimitFirstLetterLength = 16,
                                    Lowercase = true,
                                    RemoveDuplicatedTerm = true
                                })
                            .UserDefined("pinyin_first_letter_filter",
                                new PinyinTokenFilter
                                {
                                    KeepFirstLetter = true,
                                    KeepSeparateFirstLetter = false,
                                    KeepJoinedFullPinyin = false,
                                    KeepFullPinyin = false,
                                    KeepOriginal = false,
                                    LimitFirstLetterLength = 16,
                                    Lowercase = true,
                                    RemoveDuplicatedTerm = false
                                })
                        )
                        .Analyzers(aa => aa
                            .Custom("ik_analyzer", ca => ca
                                 .CharFilters("tsconvert")
                                 .Tokenizer("ik_max_word")
                             )
                            .Custom("ik_search_analyzer", ca => ca
                                 .CharFilters("tsconvert")
                                 .Tokenizer("ik_smart")
                                 .Filters("lowercase")
                             )
                            .Custom("ik_pinyin_analyzer", ca => ca
                                 .CharFilters("tsconvert")
                                 .Tokenizer("ik_max_word")
                                 .Filters("pinyin_max_filter", "lowercase")
                             )
                            .Custom("ik_pinyin_search_analyzer", ca => ca
                                 .CharFilters("tsconvert")
                                 .Tokenizer("ik_smart")
                                 .Filters("pinyin_filter", "lowercase")
                             )
                            .Custom("ik_pinyin_first_letter_analyzer", ca => ca
                                 .CharFilters("tsconvert")
                                 .Tokenizer("ik_max_word")
                                 .Filters("pinyin_first_letter_filter", "lowercase")
                             )
                        )
                ))
                .Map<SearchUniversity>(mm => mm
               .Dynamic(false)
                   .Properties(p => p
                       .Number(t => t.Name(n => n.Id))
                       .Keyword(t => t.Name(n => n.UserId))
                       .Number(t => t.Name(n => n.Type))
                       .Keyword(t => t.Name(n => n.Logo))
                       .Number(t => t.Name(n => n.ProvinceId))
                       .Keyword(t => t.Name(n => n.ProvinceName))
                       .Number(t => t.Name(n => n.CityId))
                       .Keyword(t => t.Name(n => n.CityName))
                       .Keyword(t => t.Name(n => n.EnrollmentCode))
                       .Boolean(t => t.Name(n => n.IsDeleted))
                       .Date(t => t.Name(n => n.CreateTime))
                       .Keyword(t => t.Name(n => n.FirstLetterName))
                       .Text(t => t
                           .Name(n => n.Name)
                           .Fields(f => f
                                .Text(ft => ft.Name("pinyin")
                                     .Store(false)
                                     .Analyzer("ik_pinyin_analyzer")
                                     .SearchAnalyzer("ik_pinyin_search_analyzer")
                                )
                                .Text(ft => ft.Name("fisrtletter")
                                     .Store(false)
                                     .Analyzer("ik_pinyin_first_letter_analyzer")
                                     .SearchAnalyzer("ik_pinyin_first_letter_analyzer")
                                )

                           )
                           .IndexOptions(IndexOptions.Offsets)
                       )
                   )
               )
            );

        }
    }
}
